////////////////////////////////////////////////////////////////////////////////////////////////////
//  Description: Common include file for ALL shaders 
//	 - only add when really necessary, this will propagate to ALL shaders
////////////////////////////////////////////////////////////////////////////////////////////////////

#include "FXSetupEnvVars.cfi"

#define INST_STREAM_CUSTOM   \

#define PI 3.1415
#define PI_X2 6.2831
#define PI_X4 12.5663

//=== Render targets custom ID's ================================================

#define _RT2D_WATER_ID 0
#define _RT2D_DEPTH_ID 1
#define _RT2D_SCREEN_ID 2
#define _RT2D_REFRACT_ID 3
#define _RT2D_TRANSLUCENCE_ID 4

// === HDR fake definitions =====================================================

#define HDR_OVERBRIGHT 8
#define HDR_FAKE_MAXOVERBRIGHT 16
#define HDR_EXP_OFFSET 128
#define HDR_EXP_BASE   1.06

// === Lights definitions ========================================================

#define LT_DIRECTIONAL 0
#define LT_OMNI        1
#define LT_PROJECTED   2

//=== Common definitions =========================================================

#include "FXStreamDefs.cfi"
#include "FXConstantDefs.cfi"
#include "FXSamplerDefs.cfi"

////////////////////////////////////////////////////////////////////////////////////////////////////

void ComputeGlobalFogPS(inout half3 cOut, float fDist)
{
#if %_RT_FOG
  cOut.xyz = lerp(g_PS_FogColor.xyz, cOut.xyz, fDist);
#endif  
}

// Common functions
half4 EXPAND( half4 a )
{
  return a * 2 - 1;
}
half3 EXPAND( half3 a )
{
  return a * 2 - 1;
}
half EXPAND( half a )
{
  return a * 2 - 1;
}
half2 EXPAND( half2 a )
{
  return a * 2 - 1;
}

float3 TangNormalUnnormalized(float4 Tangent, float4 Binormal)
{
  return cross(Tangent.xyz, Binormal.xyz) * Tangent.w;
}

float3 TangNormal(float4 Tangent, float4 Binormal)
{
  return normalize(TangNormalUnnormalized(Tangent, Binormal));
}

////////////////////////////////////////////////////////////////////////////////////////////////////

half3 GetBumpFromTextureColor(half4 color)
{
	half3 bump;
#if PS3
	bump.xy = color.ga;  
#elif XENON || D3D10
	bump.xy = color.yx;  
#else
	bump.xy = color.xy;  
#endif
	bump.z = 1;
	
	return bump;
}
                          
half3 GetBumpMap(sampler2D bumpMap, float2 bumpTC)
{
  return GetBumpFromTextureColor(tex2D(bumpMap, bumpTC.xy));
}

half2 FetchNormalMap(sampler2D bumpMap, float2 bumpTC)
{
  half2 bumpNormal = GetBumpMap( bumpMap, bumpTC.xy );

#if XENON
  // temporary normals quick-fix for 360 since we changed to signed format
  // this is needed mostly for very specific cases in ocean/caustics, etc
  bumpNormal = bumpNormal* 0.5 +0.5;
#endif

  return bumpNormal;
}

half3 GetNormalMapFromTextureColor(half4 color)
{
  half3 normal; 
  normal.xy = GetBumpFromTextureColor(color).xy;
#if !XENON
	// we use custom signed format for Xbox 360
  normal.xy = EXPAND(normal.xy);
#endif
  normal.z = sqrt(saturate(1.h + dot(normal.xy, -normal.xy)));  
  return normal;
}

half3 GetNormalMap(sampler2D bumpMap, float2 bumpTC)
{
  return GetNormalMapFromTextureColor(tex2D(bumpMap, bumpTC.xy));
}

half3 GetNormalMapLod(sampler2D bumpMap, float4 bumpTC)
{
  half3 bumpNormal;
#if PS3
  bumpNormal.xy = EXPAND(tex2Dlod(bumpMap, bumpTC).ga);  
#elif XENON
  bumpNormal.xy = tex2Dlod(bumpMap, bumpTC).yx;  
#elif D3D10
  bumpNormal.xy = EXPAND(tex2Dlod(bumpMap, bumpTC).yx);  
#else
  bumpNormal.xy = EXPAND(tex2Dlod(bumpMap, bumpTC).xy);  
#endif
  bumpNormal.z = sqrt(saturate(1.h + dot(bumpNormal.xy, -bumpNormal.xy)));  
  return bumpNormal;
}

half3 GetNormalMapGrad(sampler2D bumpMap, float2 bumpTC, float2 ddx_bumpTC, float2 ddy_bumpTC)
{
#if !XENON && !PS3
  half3 bumpNormal;

#if D3D10
  bumpNormal.xy = EXPAND(tex2Dgrad(bumpMap, bumpTC, ddx_bumpTC, ddy_bumpTC).yx);  
#else
	// DX9
  bumpNormal.xy = EXPAND(tex2Dgrad(bumpMap, bumpTC, ddx_bumpTC, ddy_bumpTC).xy);  
#endif

  bumpNormal.z = sqrt(saturate(1 - dot(bumpNormal.xy, bumpNormal.xy)));  

  return bumpNormal;
#else
	return half3(0, 0, 1);
#endif
}

half4 GetTexture2DGrad(sampler2D texSampler, float2 uv, float2 ddxUV, float2 ddyUV)
{
#if !XENON && !PS3
	return tex2Dgrad(texSampler, uv, ddxUV, ddyUV);
#else
	return half4(1, 1, 1, 1);
#endif
}

half DepthBias(half fHeight, half3 vNormal, half3 vView, half2 vZW)
{
  half3 vN = vNormal * fHeight;
  half fOffs = dot(vView, vN) * 0.02;
  half fOrigZ = vZW.x;
  return (fOrigZ - fOffs) / vZW.y;
}

half4 GetTexture2D(sampler2D MapSampler, float2 texTC)
{
  half4 texColor = tex2D(MapSampler, texTC.xy);

  return texColor;
}

// Sample the texture with pixel offset which is a multiple of 0.5 (upto 8)
// fTexelSize should be float in order to avoid precision artifacts
half4 GetTexture2D_Offset(sampler2D smpl, half2 txCrd, half2 offset, float2 texelSize)
{
  half4 res;

#if XENON
  float offX = offset.x;
  float offY = offset.y;
  asm
  {
    tfetch2D res, txCrd, smpl, OffsetX = offX, OffsetY = offY, UseComputedLOD = false
  };
#else
	res = tex2D(smpl, txCrd + offset*texelSize);
#endif

  return res;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Depth

#define USE_DEVICE_DEPTH

float GetDepthMap(sampler2D depthSampler, float2 ScreenTC)
{
#if PS3
	float4 fDepthRGB = tex2D(depthSampler, ScreenTC.xy);
	return dot(fDepthRGB.xyzw, g_PS_DepthFactor.xyzw);
#else
	return tex2D(depthSampler, ScreenTC.xy).x;
#endif
}

#if XENON || PS3

	float GetLinearDepth(float fDevDepth, bool bDrawNearAdjust=true, bool bScaled=false)
  {
		float2 ProjRatio;
		if (bDrawNearAdjust)
		{
		#if XENON
			float bNearDepth = step(fDevDepth, g_PS_NearestScaled.z);
		#else
			float bNearDepth = fDevDepth < g_PS_NearestScaled.z;
		#endif
			ProjRatio.xy = lerp(g_PS_ProjRatio.xy, g_PS_NearestScaled.xy, bNearDepth);
		}
		else
		{
			ProjRatio.xy = g_PS_ProjRatio.xy;
		}

    float fLinearDepth = ProjRatio.y/(fDevDepth-ProjRatio.x);

		if( bScaled )
		{
			return fLinearDepth * PS_NearFarClipDist.y;
		}

	  return fLinearDepth;
  }

  float GetDeviceDepth(float fLinearDepth)
  {
    float fDevDepth = g_PS_ProjRatio.x + g_PS_ProjRatio.y/fLinearDepth;
  	return fDevDepth;
  }

#else

#ifdef CAFE
  float GetLinearDepth(float fDevDepth, bool bDrawNearAdjust, bool bScaled)
#else
  float GetLinearDepth(float fDevDepth, bool bDrawNearAdjust=true, bool bScaled=false)
#endif
  {
		if( bScaled )
		{
			return fDevDepth * PS_NearFarClipDist.y;
		}

	  return fDevDepth;
  } 
  float GetDeviceDepth(float fLinearDepth)
  {
  	return fLinearDepth;
  }
#endif

#ifdef PS3

//////////////////////////////////////////////////////////////////////////
	float GetDeviceDepthH(sampler2D depthSampler, float2 ScreenTC)
	{
		return texDepth2D_precise(depthSampler,ScreenTC);
		//float4 fDepthChanneled = tex2D(depthSampler, ScreenTC);
		//return dot(fDepthChanneled, g_PS_DepthFactor);
	}
//////////////////////////////////////////////////////////////////////////
	float GetLinearDepthOffs(sampler2D depthSampler, float2 ScreenTC, float2 fOffset, bool bDrawNearAdjust=true)
  {
		float4 fDepthRGB = tex2D(depthSampler, ScreenTC.xy + fOffset.xy);
		float fDevDepth = dot(fDepthRGB.xyzw, g_PS_DepthFactor.xyzw);
		
		return GetLinearDepth(fDevDepth, bDrawNearAdjust);
  }
//////////////////////////////////////////////////////////////////////////
  float GetLinearDepth(sampler2D depthSampler, float2 ScreenTC, bool bDrawNearAdjust=true)
  {
    float4 fDepthRGB = tex2D(depthSampler, ScreenTC.xy);
		float fDevDepth = dot(fDepthRGB.xyzw, g_PS_DepthFactor.xyzw);
		
		return GetLinearDepth(fDevDepth, bDrawNearAdjust);
  }
//////////////////////////////////////////////////////////////////////////
	float GetLinearDepth_Offset(sampler2D depthSampler, half2 ScreenTC, half2 offset, float2 pixelSize)
	{
		return GetLinearDepth(depthSampler, ScreenTC + offset * pixelSize);
	}
//////////////////////////////////////////////////////////////////////////
  float GetLinearDepth_ProjTC(sampler2D depthSampler, float4 ProjTC, bool bDrawNearAdjust=true)
  {
    float4 fDepthRGB = tex2Dproj( depthSampler, ProjTC );
    float fDevDepth = dot(fDepthRGB.xyzw, g_PS_DepthFactor.xyzw);

		return GetLinearDepth(fDevDepth, bDrawNearAdjust);
  }

#else

	float GetDeviceDepthH(sampler2D depthSampler, float2 ScreenTC)
	{
		float Z	= tex2D(depthSampler, ScreenTC).x;
#ifndef XENON
		//on PC we just have linear depth and need to project it again
		Z	=	g_PS_ProjRatio.y/Z+g_PS_ProjRatio.x;
#endif		
		return Z;
	}

  float GetLinearDepthOffs(sampler2D depthSampler, float2 ScreenTC, float2 fOffset)
  {
    float fDepth;
    asm
    {
      tfetch2D fDepth, ScreenTC, depthSampler, OffsetX = fOffset.x, OffsetY = fOffset.y, UseComputedLOD = false
    };
#ifdef CAFE
		return GetLinearDepth(fDepth, true, false);
#else
		return GetLinearDepth(fDepth);
#endif
  }

  float GetLinearDepth(sampler2D depthSampler, float2 ScreenTC)
  {
		float fDepth = tex2D( depthSampler, ScreenTC.xy ).x;
#ifdef CAFE
		return GetLinearDepth(fDepth, true, false);
#else
		return GetLinearDepth(fDepth);
#endif
  }

	float GetLinearDepth_Offset(sampler2D depthSampler, float2 ScreenTC, half2 offset, float2 pixelSize)
	{
		float fDepth = GetTexture2D_Offset(depthSampler, ScreenTC, offset, pixelSize ).x;
#ifdef CAFE
		return GetLinearDepth(fDepth, true, false);
#else
		return GetLinearDepth(fDepth);
#endif
	}

  float GetLinearDepth_ProjTC(sampler2D depthSampler, float4 ProjTC)
  {
		float fDepth = tex2Dproj( depthSampler, ProjTC ).x;
#ifdef CAFE
		return GetLinearDepth(fDepth, true, false);
#else
		return GetLinearDepth(fDepth);
#endif
  }
#endif

float GetLinearDepthScaled( sampler2D smpDepth, float2 homogeneousPositionTexProj )
{
 	return GetLinearDepth( smpDepth, homogeneousPositionTexProj ) * PS_NearFarClipDist.y;
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Motion vector
#ifdef CAFE
float4 EncodeMotionVector(float2 vMotion, bool bFastEncode, bool bInvert)
#else
float4 EncodeMotionVector(float2 vMotion, bool bFastEncode = false, bool bInvert = false)
#endif
{
	if( bInvert )
		vMotion.y	*=	-1.f;

	if( bFastEncode )
	{
		vMotion = vMotion * 0.5h + 0.5h;
	}
	else
	{
		vMotion	=	sqrt(abs(vMotion))*sign(vMotion);
		vMotion	=	vMotion* 0.5h + 127.f/255.f;
	}

	return vMotion.xyxy;
} 
#ifdef CAFE
float4 EncodeMotionVector(float2 vMotion)
{
	return EncodeMotionVector(vMotion, false, false);
}

float2 DecodeMotionVector(float4 vMotionEncoded, bool bFastEncoded)
#else
float2 DecodeMotionVector(float4 vMotionEncoded, bool bFastEncoded = false)
#endif
{
	if( bFastEncoded )
	{
		//return vMotionEncoded*2-1;
		return (vMotionEncoded - 127.0f/255.0f) * 2.0f;
	}

	vMotionEncoded.xy = (vMotionEncoded.xy - 127.f/255.f) * 2.0f;
	return (vMotionEncoded.xy * vMotionEncoded.xy) * sign(vMotionEncoded.xy);
#ifdef CAFE
} 

float2 DecodeMotionVector(float4 vMotionEncoded)
{
	return DecodeMotionVector(vMotionEncoded, false);
#endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////


// Encode a float value into 3 bytes (input value should be in the range of [0, 1])
float3 PackFloatToVec3(const float value)
{
	static const float3 bitSh = float3(256.0 * 256.0, 256.0, 1.0);
	static const float3 bitMsk = float3(0.0, 1.0/256.0, 1.0/256.0);
	float3 res = frac(value * bitSh);
	res -= res.xxy * bitMsk;
	return res;
}

float UnpackFloatFromVec3(const float3 value)
{
	static const float3 bitSh = float3(1.0/(256.0*256.0), 1.0/256.0, 1.0);
	return dot(value, bitSh);
}

half GetDownscaledDepth(in float4 rawDepth)
{
#if XENON || PS3
	// unpack from R8G8
	// we scale depth to get it normalized to 1
	return dot(rawDepth.xy, float2(0.00390625h, 0.99609375h));//(data.x / 255 + data.y * 255/256);
#else
	return rawDepth.x;
#endif
}

half GetUnnormalizedLinearDownscaledDepth(in float4 rawDepth)
{
#if XENON || PS3
	// unpack from R8G8
	return dot(rawDepth.xy, float2(0.0001953125f, 0.0498046875f));//(data.x / 255 + data.y * 255/256) / 20;
#else
	return rawDepth.x;
#endif
}

half GetUnnormalizedLinearDownscaledDepth(in sampler2D depthSmp, in float2 screenCoord)
{
	float4 data = tex2D(depthSmp, screenCoord);
	return GetUnnormalizedLinearDownscaledDepth(data);
}

half GetDownscaledDepth(in sampler2D depthSmp, in float2 screenCoord)
{
	float4 data = tex2D(depthSmp, screenCoord);
	return GetDownscaledDepth(data);
}

float4 HPosToScreenTC(float4 HPos)
{
  float4 ScrTC = HPos;
  ScrTC.xy = (HPos.xy * float2(1,-1) + HPos.ww) * 0.5;

  // No offsets required in d3d10
#if !D3D10 && !PS3 && !CAFE
  ScrTC.xy += g_VS_ScreenSize.zw*HPos.w;
#endif

  return ScrTC;
}

#if !PS3 && !XENON
sampler2D normalsSampler2D = sampler_state
{
  Texture = EngineAssets/ScreenSpace/NormalsFitting.dds;
  MinFilter = POINT;
  MagFilter = POINT;
  MipFilter = POINT;
  AddressU = Clamp;
  AddressV = Clamp;
};
#endif

void CompressUnsignedNormalToNormalsBuffer(inout half4 vNormal)
{
#if !PS3 && !XENON && !CAFE
	#if !%DETAIL_BUMP_MAPPING && !%_RT_VEGETATION && !%_RT_SHADER_LOD && !%_RT_ALPHABLEND && !%_RT_ALPHATEST
		// expand from unsigned
		vNormal.rgb = vNormal.rgb * 2.h - 1.h;

		// renormalize (needed if any blending or interpolation happened before)
		vNormal.rgb = normalize(vNormal.rgb);
		// get unsigned normal for cubemap lookup (note the full float presision is required)
		half3 vNormalUns = abs(vNormal.rgb);
		// get the main axis for cubemap lookup
		half maxNAbs = max(vNormalUns.z, max(vNormalUns.x, vNormalUns.y));
		// get texture coordinates in a collapsed cubemap
		float2 vTexCoord = vNormalUns.z<maxNAbs?(vNormalUns.y<maxNAbs?vNormalUns.yz:vNormalUns.xz):vNormalUns.xy;
		vTexCoord = vTexCoord.x < vTexCoord.y ? vTexCoord.yx : vTexCoord.xy;
		vTexCoord.y /= vTexCoord.x;
		// fit normal into the edge of unit cube
		vNormal.rgb /= maxNAbs;

		// look-up fitting length and scale the normal to get the best fit
	#if PS3 || XENON || CAFE
		float fFittingScale = tex2D(normalsSampler2D, vTexCoord).a;
	#else
		#if %_RT_QUALITY || %_RT_QUALITY1
			float fFittingScale = tex2Dlod(normalsSampler2D, float4(vTexCoord, 0, 0)).a;
		#else
			float fFittingScale = tex2D(normalsSampler2D, vTexCoord).a;
		#endif
	#endif

		// scale the normal to get the best fit
		vNormal.rgb *= fFittingScale;

		// squeeze back to unsigned
		vNormal.rgb = vNormal.rgb * .5h + .5h;
	#endif
#endif
}

void ReconstructNormalFromNormalsBuffer(inout half4 vNormal, bool bNormalize)
{
	// renormalization is required on PC because of fitted normal's length
#if PS3 || XENON || CAFE
	if(bNormalize)
#endif
		vNormal.xyz = normalize( vNormal.xyz );
}

// Encode 3bit dynamic object mask with gloss into single 8 bit channel.
void EncodeDynObjectMask(inout half fGloss)
{
	if(SnowVolumeParams[1].w)
	{
		bool bStatObj = SnowVolumeParams[0].x;	
		#if %_RT_SKELETON_SSD || %_RT_MORPHTARGET || %_RT_SHAPEDEFORM || %DETAIL_BENDING || %_VT_WIND || %_VT_TYPE_MODIF || %_VT_TYPE || %_VT_DET_BEND || %_VT_BEND
			// Bending objects are not considered static.
			bStatObj = false;
		#endif
		int nEnc = bStatObj * 32 + int(saturate(fGloss) * 255) / 8;
		fGloss = half(nEnc) / 255.h;
	}
}

int DecodeDynObjectMask(in half fEnc, out half fGloss)
{
	const int nEnc = int(fEnc * 255.h);
	fGloss = half(nEnc % 32) / 31.h;
	return nEnc / 32;
}

half4 GetWorldSpaceNormals( half4 vNormals, bool bNormalize )
{
	// expand from unsigned
  vNormals.xyz = EXPAND(vNormals.xyz); 

	ReconstructNormalFromNormalsBuffer(vNormals, bNormalize);

  return vNormals;
}

half4 GetWorldSpaceNormals( sampler2D smpNormals, float2 tc, bool bNormalize )
{
  half4 vNormals = tex2D( smpNormals, tc.xy );
	return GetWorldSpaceNormals(vNormals, bNormalize);
}

////////////////////////////////////////////////////////////////////////////////////////////////////

float  NumInstructions		: PI_NumInstructions;

sampler2D DebugMipColorsDiffuseSampler = sampler_state
{
 Texture = $MipColors_Diffuse;
};
sampler2D DebugMipColorsBumpSampler = sampler_state
{
 Texture = $MipColors_Bump;
};

float4 DVColor(float2 d)
{ 
  float Reso = 512;
  float TargetDeriv = 1.0f/Reso;
  float HalfTD = (TargetDeriv*0.5);
  float TwoTD = (TargetDeriv*2.0);


  float4 dd = float4(0,0,0,1);
  if (d.x > TwoTD) { dd.x = 1.0;}
  if (d.y > TwoTD) { dd.y = 1.0;}
  if (d.x < HalfTD) { dd.z = 1.0;}
  return(dd);
}


void DebugOutput(out half4 Color, in float4 baseTC)
{
  Color = 0;
 #if %_RT_DEBUG0 && !%_RT_DEBUG1 && !%_RT_DEBUG2 && !%_RT_DEBUG3 // 1000
  float2 dd = 64.0 * (abs(ddx(baseTC.xy)) + abs(ddy(baseTC.xy)));
  Color = float4(dd, 0, 1);
 #elif !%_RT_DEBUG0 && %_RT_DEBUG1 && !%_RT_DEBUG2 && !%_RT_DEBUG3 // 0100
  float4 dd = baseTC;
  Color = dd;
 #elif %_RT_DEBUG0 && %_RT_DEBUG1 && !%_RT_DEBUG2 && !%_RT_DEBUG3 // 1100
  Color = DVColor(abs(ddx(baseTC.xy)));
 #elif !%_RT_DEBUG0 && !%_RT_DEBUG1 && %_RT_DEBUG2 && !%_RT_DEBUG3 // 0010
  Color = DVColor(abs(ddy(baseTC.xy)));
 #elif %_RT_DEBUG0 && !%_RT_DEBUG1 && %_RT_DEBUG2 && %_RT_DEBUG3 // 1011
  Color = DVColor(abs(ddy(baseTC.xy)));
 #elif !%_RT_DEBUG0 && %_RT_DEBUG1 && %_RT_DEBUG2 && !%_RT_DEBUG3 // 0110
  float Reso = 512;
  float TargetDeriv = 1.0/Reso;
  float Diagonal = sqrt(2.0*TargetDeriv*TargetDeriv);
  float HalfDiag = (Diagonal*0.5);
  float TwoDiag = (Diagonal*2.0);
  float2 dx = ddx(baseTC.xy);
  float2 dy = ddy(baseTC.xy);
  float d = sqrt(dot(dx,dx) + dot(dy,dy));
  float4 dd = float4(0,0,0,1);
  if (d > TwoDiag) { dd.x = 1.0;}
  if (d > TwoDiag) { dd.y = 1.0;}
  if (d < HalfDiag) { dd.z = 1.0;}
  Color = dd;
 #elif %_RT_DEBUG0 && %_RT_DEBUG1 && %_RT_DEBUG2 && !%_RT_DEBUG3 // 1110
  float4 mipColor = tex2D(DebugMipColorsDiffuseSampler, baseTC.xy);
  Color = mipColor;
 #elif !%_RT_DEBUG0 && !%_RT_DEBUG1 && !%_RT_DEBUG2 && %_RT_DEBUG3 // 0001
  float4 mipColor = tex2D(DebugMipColorsBumpSampler, baseTC.xy);
  Color = mipColor;
 #elif %_RT_DEBUG0 && %_RT_DEBUG1 && %_RT_DEBUG2 && %_RT_DEBUG3 // 1111
  Color.xyz = NumInstructions;	// Measure overdraw
 #elif !%_RT_DEBUG0 && !%_RT_DEBUG1 && %_RT_DEBUG2 && %_RT_DEBUG3 // 0011
  float x = frac(baseTC.x);
  float y = frac(baseTC.y);
  if((x < 0.5 && y < 0.5) || (x > 0.5 && y > 0.5))
	Color = float4(3, 3, 0, 1);
  else
	Color = float4(0, 0, 3, 1);

	#ifdef PS3
	Color.a = 0.075f; // some guess value for rgbk
	#endif

 #endif
 // free:
  // 1010
  // 1001
  // 0101
  // 0111
  // 1101
}

////////////////////////////////////////////////////////////////////////////////////////////////////

half4 tex2DTerrain(sampler2D s, float2 terrainTC)
{
  return tex2D(s, terrainTC);

/*  half4 terrainColor = tex2D(s, terrainTC);
	float fScale=terrainColor.b;
	terrainColor.r += 0.001012;		// minor improvement in color reproduction
	terrainColor.r *= 31.0f/30.0f;		// 
	terrainColor.g *= 63.0f/63.0f;		//
	terrainColor.b = 1.0f-terrainColor.r-terrainColor.g;
	terrainColor.rgb *= 3.0f*fScale;
	return saturate(terrainColor);*/
}

////////////////////////////////////////////////////////////////////////////////////////////////////

float4 GetInputColor(in float4 Color)
{
 #ifdef PS3
  return Color.yzwx;
 #endif
 #if COLOR_SWAP
  return Color.zyxw;
 #else
  return Color;
 #endif
}

#define QUALITY_LOW      0
#define QUALITY_MEDIUM   1
#define QUALITY_HIGH   	 2
#define QUALITY_VERYHIGH 3

int GetShaderQuality()
{
  int nQuality;
#if !%_RT_QUALITY && !%_RT_QUALITY1
   nQuality = QUALITY_LOW;
#elif %_RT_QUALITY && !%_RT_QUALITY1
   nQuality = QUALITY_MEDIUM;
#elif !%_RT_QUALITY && %_RT_QUALITY1
   nQuality = QUALITY_HIGH;
#else
   // #warning Unknown shader quality mode
   nQuality = QUALITY_VERYHIGH;
#endif  
  return nQuality;
}

int GetMSAASampleNum()
{
  int nSamplesNum = 0;
#if %_RT_MSAA_QUALITY && %_RT_MSAA_QUALITY1
   nSamplesNum = 8;
#elif %_RT_MSAA_QUALITY && !%_RT_MSAA_QUALITY1
   nSamplesNum = 4;
#elif !%_RT_MSAA_QUALITY && %_RT_MSAA_QUALITY1
   nSamplesNum = 2;
#endif  
  return nSamplesNum;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

float ComputeTextureLOD(in float2 uv, in float2 texDim)
{
	uv *= texDim;
	
	float2 ddx_ = ddx(uv);
	float2 ddy_ = ddy(uv);
	//float2 mag = ddx_ * ddx_ + ddy_ * ddy_;
	float2 mag = abs(ddx_) + abs(ddy_);
	
	//float lod = max(0.5 * log2(max(mag.x, mag.y)), 0);
	float lod = log2(max(mag.x, mag.y));
	return lod;
}

float ComputeTextureLOD(in float2 uv, in float2 texDim, out float2 ddx_uv, out float2 ddy_uv)
{
	//float2 uvScaled = uv * texDim;
	//float2 ddx_, ddy_;
		
	//float4(ddx_, ddx_uv) = ddx(float4(uvScaled, uv));
	//float4(ddy_, ddy_uv) = ddy(float4(uvScaled, uv));	
	
	////float2 mag = ddx_ * ddx_ + ddy_ * ddy_;
	//float2 mag = abs(ddx_uv) + abs(ddy_uv);

	ddx_uv = ddx(uv);
	ddy_uv = ddy(uv);
	//float2 mag = (ddx_uv * ddx_uv + ddy_uv * ddy_uv) * texDim * texDim;
	float2 mag = (abs(ddx_uv) + abs(ddy_uv)) * texDim;
	
	//float lod = max(0.5 * log2(max(mag.x, mag.y)), 0);
	float lod = log2(max(mag.x, mag.y));
	return lod;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

#if XENON

float4 rgb10a2_to_rgb10a2f( float4 InColor )
{
	// Shift left 3 bits. This allows us to have the exponent and mantissa on opposite
	// sides of the decimal point for extraction.
	float4 OutColor = InColor * 8.0f;

	// Extract the exponent and mantissa that are now on opposite sides of the decimal point
	float3 e = floor( OutColor.rgb );
	float3 m = frac( OutColor.rgb );
	
	// Perform the 7e3 conversion.  Note that this varies on the value of e for each channel:
	// if e != 0.0f then the correct conversion is (1+m)/8*pow(2,e).
	// else it is (1+m)/8*pow(2,e).  
	OutColor.rgb  = (e == 0.0f) ? 2*m/8 : (1+m)/8 * exp2(e);

	return OutColor;
}

// Fetches an rgb10a2f texture aliased as a rgb10a2 texture - check xbox documentation (7e3) for more details
//  - helpfull for specific cases to decrease BW (at cost of ALU) and also to minimize resolve cost
//  - beware that this doens't support correct bilinear filtering fetches
float4 tex2D_rgb10a2f( sampler2D s, float2 vTexCoord )
{
	return  rgb10a2_to_rgb10a2f( tex2D(s, vTexCoord ) );
}

#endif

////////////////////////////////////////////////////////////////////////////////////////////////////

half GetLuminance( half3 color )
{
	return dot( color, half3( 0.2126h, 0.7152h, 0.0722h ) );
}

float EncodeSceneUV(in const float2 uv)
{
#ifdef D3D11
	const uint2 coord = uint2(int2(uv * 4096.0f) + 32768);
	const uint enc = (coord.x << 16) | (coord.y & 0xFFFF);
	return asfloat(enc);
#else
	return 0;
#endif
}

float2 DecodeSceneUV(in const float uvPacked)
{
#ifdef D3D11
	const uint enc = asuint(uvPacked);
	const int2 coord = int2(enc >> 16, enc & 0xFFFF) - 32768;
	return (float2) coord / 4096.0f;
#else
	return float2(0, 0);
#endif
}

float2 ComputeCloudShadowMapUV(in float3 worldPos)
{
	const float3 sunDir = normalize(float3(0.1, 0.1, 1.0)); // = g_PS_SunLightDir.xyz;
	const float t = -worldPos.z / sunDir.z;
	return worldPos.xy + t * sunDir.xy;
}